﻿using Devonline.Logging;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Logging;
using Yarp.ReverseProxy.Transforms;

namespace Devonline.Identity.Gateway;

public static class ServiceExtensions
{
    /// <summary>
    /// 注册网关服务
    /// </summary>
    /// <param name="builder"></param>
    /// <param name="appSetting"></param>
    /// <returns></returns>
    public static IServiceCollection AddGatewayServices(this WebApplicationBuilder builder, GatewaySetting appSetting)
    {
        builder.WebHost.ConfigureKestrel(appSetting);
        builder.Host.AddLogging();

        var services = builder.Services;
        services.AddReverseProxy(builder.Configuration, appSetting);
        services.AddLogging();
        services.AddControllers();
        services.AddHttpContextAccessor();
        services.AddResponseCompression(x => x.EnableForHttps = true);
        if (appSetting.HttpLogging is not null)
        {
            services.AddHttpLogging(options => options = appSetting.HttpLogging);
        }

        if (appSetting.W3CLogger is not null)
        {
            services.AddW3CLogging(options => options = appSetting.W3CLogger);
        }

        services.AddGatewayAuthentication(appSetting);
        //services.AddDefaultAuthorization(appSetting);
        services.AddAuthorization();
        services.AddDefaultDataProtection(appSetting);

        return services;
    }

    /// <summary>
    /// 注册认证服务
    /// 目前仅有 Token 类型在使用 YARP 做网关反向代理时通过了下游测试
    /// </summary>
    /// <param name="services">依赖注入容器服务</param>
    /// <param name="appSetting">配置项</param>
    /// <returns></returns>
    public static IServiceCollection AddGatewayAuthentication(this IServiceCollection services, GatewaySetting appSetting)
    {
        switch (appSetting.AuthType)
        {
            case AuthType.Cookie:
                // TODO TBC Cookie 类型但应用测试通过
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
                break;
            case AuthType.Token:
                {
                    // TODO TBC 暂时不能直接复制, 尚不清楚那些值赋值不对
                    //.AddJwtBearer(options => options = jwtBearerOptions);
                    var tokenOptions = appSetting.TokenOptions;
                    ArgumentNullException.ThrowIfNull(tokenOptions);
                    tokenOptions.Configure();
                    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => options.TokenValidationParameters = tokenOptions.TokenValidationParameters);
                    break;
                }
            case AuthType.Oidc:
                services.AddOpenIdConnects(appSetting.OpenIdConnects!);
#if DEBUG
                IdentityModelEventSource.ShowPII = true;
#endif
                break;
            default:
                services.AddAuthentication();
                break;
        }

        return services;
    }

    /// <summary>
    /// 添加网关反向代理
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <param name="appSetting"></param>
    /// <returns></returns>
    public static IServiceCollection AddReverseProxy(this IServiceCollection services, IConfiguration configuration, GatewaySetting appSetting)
    {
        services.AddReverseProxy()
        .LoadFromConfig(configuration.GetSection(appSetting.ReverseProxy));
        //.AddTransforms(builderContext => builderContext.AddRequestTransform(requestContext => requestContext.SetGatewayIdentityUserClaimsToRequestHeader()));

        return services;
    }
    /// <summary>
    /// 在请求转发时, 将已认证用户关键信息传递到下游应用
    /// </summary>
    /// <param name="requestContext"></param>
    /// <returns></returns>
    public static ValueTask SetGatewayIdentityUserClaimsToRequestHeader(this RequestTransformContext requestContext)
    {
        if (requestContext.HttpContext.User.Identity?.IsAuthenticated ?? false)
        {
            requestContext.ProxyRequest.Headers.Add(HTTP_HEADER_GATEWAY_USER_PREFIX + CLAIM_TYPE_USER_ID, requestContext.HttpContext.GetUserId());
            requestContext.ProxyRequest.Headers.Add(HTTP_HEADER_GATEWAY_USER_PREFIX + CLAIM_TYPE_USER_NAME, requestContext.HttpContext.GetUserName());
        }

        return ValueTask.CompletedTask;
    }
}